﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Text.Editor;
using FunctionPoint.Common;
using FunctionPoint.UI;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System.Diagnostics;
using EnvDTE;
using EnvDTE80;

using System.Management;
using FunctionPoint.Utility;
using Microsoft.VisualStudio.TeamFoundation;
using System.Windows.Input;
using Microsoft.VisualStudio.Text;
using System.Windows;
using FunctionPoint.Services;
using System.Collections.ObjectModel;
using Microsoft.TeamFoundation.VersionControl.Common;
using FunctionPoint.Adornments;
using Microsoft.VisualStudio.Text.Formatting;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Utilities;
using FunctionPoint.Adornment;
using System.Windows.Controls;
using Microsoft.TeamFoundation.Common;
using Microsoft.VisualStudio.TeamFoundation.WorkItemTracking;

namespace FunctionPoint.UIManagers
{

    /// <summary>
    /// The adornment manager belongs to a particular WpfTextView.  Its purpose is the management of painting the objects based on either being 
    /// dropped on the editor service from the DropHandler.  It will repspond to requests from the owning service to paint or not paint certain work items
    /// 

    public class WpfTextViewAdornmentManager : DependencyObject
    {




        // private List<FunctionPointLocationAdornment> functionPointLocationAdornments;
        private IWpfTextView wpfTextView;
        private EnvDTE.DTE vsShellDTE;
        private EnvDTE80.DTE2 vsshellDTE2;
        //[Export(typeof(AdornmentLayerDefinition))]
        //[Name(FunctionPoint.Common.Constants.VIEWPORT_ADORNMENTLAYERNAME)]
        //[Order(After = PredefinedAdornmentLayers.Selection)]
        //[TextViewRole(PredefinedTextViewRoles.Document)]
        private IAdornmentLayer _adornmentLayer;
        private IFunctionPointManagerService _managementService = null;
        #region DependencyProperties



        #endregion

        #region Properties

        public IFunctionPointManagerService FunctionPointManagementService
        {
            get
            {
                if (_managementService == null)
                    _managementService = ServiceProvider.GlobalProvider.GetService(typeof(SFunctionPointManagerService)) as IFunctionPointManagerService;

                return _managementService;
            }
        }
        public IAdornmentLayer AdornmentLayer
        {
            get
            {
                if (_adornmentLayer == null)
                    _adornmentLayer = wpfTextView.GetAdornmentLayer(FunctionPoint.Common.Constants.VIEWPORT_ADORNMENTLAYERNAME);

                return _adornmentLayer;
            }
        }

        
        #endregion

        #region Constructors -------------------------------------


        public WpfTextViewAdornmentManager(IWpfTextView textView, IServiceProvider serviceProvider)
        {

            wpfTextView = textView;

            //functionPointLocationAdornments = new List<FunctionPointLocationAdornment>();

            textView.LayoutChanged += new EventHandler<TextViewLayoutChangedEventArgs>((sender, args) =>
            {
                RepaintTextViewAdornments(args.NewSnapshot);

            });

        }

        #endregion


        public void RepaintTextViewAdornments(ITextSnapshot snapShot)
        {
            //TODO: It may be more beneficial to not recreate this every time. Especially if there are several work items selected.
            // howver, it does only apply to the visual elements.  still.
            AdornmentLayer.RemoveAllAdornments();

            var selectedWorkItems = FunctionPointManagementService.GetWorkItemsForDisplay();

            selectedWorkItems.ToList().ForEach(workItem =>
            {

                // need to determine if this even affects the page in question.  

                string currentFilePath = wpfTextView.GetFilePath();
                string serverPath = FunctionPointManagementService.GetServerPath(currentFilePath);

                
                if (workItem.ItemPath == serverPath)
                {
                    WorkItemAdornment workItemAdornment = new WorkItemAdornment(workItem, wpfTextView, snapShot);

                    // need to detrmine if it's in the visual span.

                    ITextViewLine adornmentTextViewLine = wpfTextView.GetTextViewLineContainingBufferPosition(
                        workItemAdornment.TrackingPoint.GetPoint(snapShot));

                    if (adornmentTextViewLine != null && (adornmentTextViewLine.VisibilityState == VisibilityState.PartiallyVisible || adornmentTextViewLine.VisibilityState == VisibilityState.FullyVisible))
                    {

                        Canvas.SetLeft(workItemAdornment.VisualElement, adornmentTextViewLine.Left);
                        Canvas.SetTop(workItemAdornment.VisualElement, adornmentTextViewLine.Top);

                        AdornmentLayer.AddAdornment(AdornmentPositioningBehavior.ViewportRelative,
                            null, null, workItemAdornment.VisualElement,
                            new AdornmentRemovedCallback((sender, uiElement) =>
                            {
                                Debug.WriteLine(string.Format("Adornment Removal Called:{0}, From: {1}", workItemAdornment.WorkItem.Title, sender));
                            }));
                    }
                }
            });


        }

        #region Code Discovery
        /// <summary>
        /// Returns an adjusted CodeElement. Walks comments 'down' to the next real element.
        /// </summary>
        /// <remarks>Courtesy Rick Strahl:
        /// http://www.west-wind.com/weblog/posts/2005/Nov/03/Retrieving-a-CodeElement-reliably-in-VSNET-2005
        /// </remarks>
        /// <returns></returns>
        private void CodeDiscovery()
        {

            EnvDTE.ProjectItem projectItem = vsShellDTE.ActiveDocument.ProjectItem;
            EnvDTE.FileCodeModel codeModel = projectItem.FileCodeModel as EnvDTE.FileCodeModel;
            EnvDTE80.FileCodeModel2 codeModel2 = projectItem.FileCodeModel as EnvDTE80.FileCodeModel2;

            EnvDTE.TextSelection selection = (EnvDTE.TextSelection)projectItem.Document.Selection; // vsShellDTE.Document.Selection;
            IVsShell2 shell2 = null;

            EnvDTE.vsCMElement scopes = EnvDTE.vsCMElement.vsCMElementFunction & EnvDTE.vsCMElement.vsCMElementClass & EnvDTE.vsCMElement.vsCMElementInterface & EnvDTE.vsCMElement.vsCMElementNamespace;

            try
            {

                var activeWindowName = vsShellDTE.ActiveWindow.Caption;
                var mainWindowCaption = vsShellDTE.MainWindow.Caption;

                EnvDTE.CodeElement pointElement = codeModel.CodeElementFromPoint(selection.ActivePoint, scopes);

                if (pointElement != null)
                {
                    if (pointElement.IsCodeType)
                    {
                        Debug.WriteLine("CodeModel2 CodeElemenetFromPoint: {0}, Kind: {1}, Name:{2}, ActivePoint:{3}", pointElement.FullName, pointElement.Kind, pointElement.Name, vsShellDTE.ActiveWindow.Caption);
                    }
                    else
                        Debug.WriteLine("CodeModel2 Not a code type");
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception..." + ex.Message + "\r\n" + ex.StackTrace);
            }


            //foreach (EnvDTE.vsCMElement scope in Enum.GetValues(scopes.GetType()))
            //{
            //    try
            //    {

            //        EnvDTE.CodeElement codeElement = codeModel2.CodeElementFromPoint(selection.ActivePoint, scope);

            //        if (codeElement != null)
            //            Debug.WriteLine("CodeElement2: {0}\r\nScope:{1}\r\nKind:{2}\r\nLanguage:{3}\r\nInfoLocation:{4}",  codeElement.FullName, scope.ToString(), codeElement.Kind, codeElement.Language, codeElement.InfoLocation  );
            //    }
            //    catch (Exception ex)
            //    {
            //        //Debug.WriteLine(
            //        //    string.Format("Exception and foreach codeElement in codeModel.CodeElements:\r\n{0}\r\n\r\n{1}", ex.Message, ex.StackTrace));
            //    }

            //}
        }
        public CodeElement GetCodeElement(CodeLanguage codeLanguage)
        {

            TextSelection selection =
                  (TextSelection)this.vsShellDTE.ActiveDocument.ActiveWindow.Selection;

            EditPoint selPoint = selection.ActivePoint.CreateEditPoint();

            selPoint.StartOfLine();

            while (true)
            {

                string BlockText = selPoint.GetText(selPoint.LineLength).Trim();

                // *** Skip over any XML Doc comments
                if (codeLanguage == CodeLanguage.CSharp && BlockText.StartsWith("//") ||
                      codeLanguage == CodeLanguage.VB && BlockText.StartsWith("' "))
                {
                    selPoint.LineDown(1);
                    selPoint.StartOfLine();
                }
                else
                    break;

            }


            // *** Make sure the cursor is placed inside of the definition always
            // *** Especially required for single line methods/fields/events etc. 

            selPoint.EndOfLine();
            selPoint.CharLeft(1);  // Force into the text 

            // get the element under the cursor

            CodeElement element = this.vsShellDTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElementFromPoint(selPoint, 0);

            return element;

        }
        #endregion
    }
}
